using System;
using System.IO;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Logging;
using RBAC.Domain.WarehouseManagement;
using RBAC.Interstructrue;

namespace RBAC.Write.Api.Middlewares
{
    /// <summary>
    /// 操作日志中间件：记录每次请求的关键信息到 LoggingTable
    /// </summary>
    public class OperationLogMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger<OperationLogMiddleware> _logger;

        public OperationLogMiddleware(RequestDelegate next, ILogger<OperationLogMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task InvokeAsync(HttpContext context, RBACDbContext db)
        {
            var start = DateTime.UtcNow;
            string requestBody = string.Empty;

            // 仅对非GET请求尝试读取Body（避免对大文件上传造成压力）
            if (!HttpMethods.IsGet(context.Request.Method) && context.Request.ContentLength.HasValue && context.Request.ContentLength.Value < 1024 * 1024)
            {
                context.Request.EnableBuffering();
                using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true);
                requestBody = await reader.ReadToEndAsync();
                context.Request.Body.Position = 0;
            }

            int? statusCode = null;
            try
            {
                await _next(context);
                statusCode = context.Response?.StatusCode;
            }
            finally
            {
                try
                {
                    var elapsedMs = (int)(DateTime.UtcNow - start).TotalMilliseconds;

                    // 用户信息：优先从 Claims，其次从 Header，最后匿名
                    var userIdStr = context.User?.FindFirstValue(ClaimTypes.NameIdentifier) ?? context.Request.Headers["X-User-Id"].ToString();
                    int.TryParse(userIdStr, out var userId);
                    var userName =context.User?.Identity?.Name
                        ?? context.User?.FindFirstValue(ClaimTypes.Name)
                        ?? context.User?.FindFirstValue("userName")
                        ?? context.User?.FindFirstValue("preferred_username")
                        ?? context.User?.FindFirstValue("unique_name")
                        ?? context.User?.FindFirstValue(ClaimTypes.Email)
                        ?? context.User?.FindFirstValue("sub")
                        ?? context.Request.Headers["X-User-Name"].ToString()
                        ?? "Anonymous";
                        
                    var operation = new
                    {
                        Method = context.Request.Method,
                        Path = context.Request.Path.ToString(),
                        Query = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : string.Empty,
                        Body = requestBody,
                        StatusCode = statusCode,
                        ClientIP = context.Connection.RemoteIpAddress?.ToString(),
                        ElapsedMs = elapsedMs
                    };

                    var log = new LoggingTable
                    {
                        UserId = userId,
                        UserName = userName,
                        OperationContent = JsonSerializer.Serialize(operation),
                        CreateTime = DateTime.Now,
                        IsDeleted = false
                    };

                    db.Set<LoggingTable>().Add(log);
                    await db.SaveChangesAsync();
                }
                catch (Exception ex)
                {
                    // 持久化日志失败不影响主流程
                    _logger.LogError(ex, "保存操作日志失败");
                }
            }
        }
    }

    public static class OperationLogMiddlewareExtensions
    {
        public static IApplicationBuilder UseOperationLogging(this IApplicationBuilder app)
        {
            return app.UseMiddleware<OperationLogMiddleware>();
        }
    }
}
